package org.datascooter.bundle.attribute;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.datascooter.exception.DataScooterException;
import org.datascooter.utils.DBType;
import org.datascooter.utils.LangUtils;

/**
 * This persistable attribute may be restored into instance variable via method access
 * 
 * @author nemo
 * 
 */
public class MethodReference extends PersistReferenceAttribute {

	protected Method getter;
	protected Method setter;

	public MethodReference(String name, String entity, String column, DBType type, int scale, int precision, boolean insertable, boolean updatable,
		boolean nullable) {
		super(name, entity, column, type, scale, precision, insertable, updatable, nullable);
	}

	@SuppressWarnings("unchecked")
	@Override
	public MethodReference copy() {
		return new MethodReference(name, getEntity(), getColumn(), getType(), getScale(), getPrecision(), insertable, updatable, isNullable());
	}

	@SuppressWarnings("unchecked")
	@Override
	public MethodReference copyMod(DBType dbType) {
		return new MethodReference(name, getEntity(), getColumn(), dbType, scale, precision, insertable, updatable, isNullable());
	}

	@Override
	public void explore(Class<?> clazz) throws SecurityException, NoSuchMethodException {
		if (name.startsWith(GET)) {
			getter = clazz.getMethod(name);
		} else {
			getter = clazz.getMethod(GET + LangUtils.capitalize(name));
		}
		setGetClass(getter.getReturnType());
		if (name.startsWith(SET)) {
			try {
				setter = clazz.getMethod(name, new Class[] {
					getClass
				});
			} catch (NoSuchMethodException e) {
				setter = clazz.getMethod(name, new Class[] {
					String.class
				});
			}
		} else {
			try {
				setter = clazz.getMethod(SET + LangUtils.capitalize(name), new Class[] {
					getClass
				});
				if (setter != null && getClass.isPrimitive() && nullable) {
					throw new DataScooterException("***Possible NPE by insert null values - setter has primitive argument and field is nullable ");
				}
			} catch (NoSuchMethodException e) {
				setter = clazz.getMethod(SET + LangUtils.capitalize(name), new Class[] {
					String.class
				});
			}
		}
		setSetClass(setter.getParameterTypes()[0]);
	}

	@Override
	public String getActorName() {
		return getter.getName();
	}

	@Override
	protected Object invoke(Object obj) throws IllegalAccessException, InvocationTargetException {
		return getter.invoke(obj);
	}

	@Override
	protected void revoke(Object obj, Object param) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		setter.invoke(obj, param);
	}
}
